#pragma once

#include "online.hpp"
#include "util.hpp"
#include <unordered_map>

typedef enum
{
    UNLOGIN,
    LOGIN_OK
} ss_statu;
class session
{
public:
    session(uint64_t ssid) : _ssid(ssid), _statu(UNLOGIN)
    {
        DBG_LOG("SESSION %p 被创建 ", this);
    }
    uint64_t get_ssid()
    {
        return _ssid;
    }
    void set_statu(ss_statu statu)
    {
        _statu = statu;
    }
    void set_user(uint64_t uid)
    {
        _uid = uid;
    }
    uint64_t get_user()
    {
        return _uid;
    }
    bool is_login()
    {
        return (_statu == LOGIN_OK);
    }
    void set_timer(const wsserver_t::timer_ptr &tp)
    {
        _tp = tp;
    }
    wsserver_t::timer_ptr &get_timer()
    {
        return _tp;
    }
    ~session()
    {
        DBG_LOG("SESSION %p 被销毁 ", this);
    }

private:
    uint64_t _ssid;            // session的id
    uint64_t _uid;             // 用户的id，可以在数据库中查找用户的信息
    ss_statu _statu;           // 登录状态
    wsserver_t::timer_ptr _tp; // 定时器功能
};

#define SESSION_TIMEOUT 30000
#define SESSION_FOREVER -1

using session_ptr = std::shared_ptr<session>;

class session_manager
{
public:
    session_manager(wsserver_t *srv) : _next_ssid(1), _server(srv)
    {
        DBG_LOG("session管理器初始化完毕");
    }
    // 与定时器的取消打配合的，不能直接去insert
    void append_session(session_ptr& ssp)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _session.insert(std::make_pair(ssp->get_ssid(),ssp));
    }
    // 创建会话
    session_ptr create_session(uint64_t uid, ss_statu statu)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        // new一个session对象
        session_ptr ssp(new session(_next_ssid));
        ssp->set_statu(statu);                            // 设置状态
        ssp->set_user(uid);
        _session.insert(std::make_pair(_next_ssid, ssp)); // 入
        _next_ssid++;
        return ssp;
    }
    // 通过sessionID找session
    session_ptr get_session_by_ssid(uint64_t ssid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _session.find(ssid);
        if (it == _session.end())
        {
            return session_ptr();
        }
        return it->second;
    }
    // 设置过期时间
    void set_session_expire_time(uint64_t ssid, int ms)
    {
        // 依赖于websocketpp的定时器来完成session生命周期的管理
        // 比如我在登陆完毕进入大厅，总不能session的生命周期到了，直接给我退出登陆了，这是不合理的，此时一定要是永久存在
        // 只有当我下完棋了，退出登陆了，才会变为有生命周期的状态
        session_ptr ssp = get_session_by_ssid(ssid);
        if (ssp.get() == nullptr)
        {
            return;
        }
        wsserver_t::timer_ptr tp = ssp->get_timer();
        // 1.在session永久存在的情况下，设置永久存在
        if (tp.get() == nullptr && ms == SESSION_FOREVER) // 定时器为空或者时间为-1，代表永久存在
        {
            return; // 本身就是永久存在了
        }
        else if (tp.get() == nullptr && ms != SESSION_FOREVER)
        {
            // 2.在session永久存在的情况下，设置指定时间之后被删除的定时任务
            wsserver_t::timer_ptr tmp_tp = _server->set_timer(ms,
                std::bind(&session_manager::remove_session,this,ssid));
            ssp->set_timer(tmp_tp);
        }
        else if(tp.get() != nullptr && ms == SESSION_FOREVER)
        {
            // 3.在session设置了定时删除的情况下，将session设置为永久存在
            // 删除定时任务会导致任务直接被执行，在session管理器中，就会直接被剔除掉
            tp->cancel();// 注意这个取消的操作并不是立即执行的，也就是说，后面的insert可能会在cancel前操作
            // 重新给session管理器添加这个信息
            ssp->set_timer(wsserver_t::timer_ptr());
            //注意不要直接去添加,需要使用定时器去添加
            // _session.insert(std::make_pair(ssp->get_ssid(),ssp));//重新设置进去
            _server->set_timer(0,std::bind(&session_manager::append_session,this,ssp));//这里的0，也不是立即执行的
        }
        else if(tp.get() != nullptr && ms != SESSION_FOREVER)
        {
            // 4.在session设置了定时删除的情况下，将session重置删除时间
            tp->cancel();//在map里删除了，后面要重新添加
            ssp->set_timer(wsserver_t::timer_ptr());
            _server->set_timer(0,std::bind(&session_manager::append_session,this,ssp));//这里的0，也不是立即执行的
            //设置定时器
            wsserver_t::timer_ptr tmp_tp = _server->set_timer(ms,
                std::bind(&session_manager::remove_session,this,ssid));
            // 重新设置session相关的定时器
            ssp->set_timer(tmp_tp);
        }
    }
    // 销毁session
    void remove_session(uint64_t ssid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _session.erase(ssid);
    }
    ~session_manager()
    {
        DBG_LOG("session管理器即将销毁");
    }

private:
    uint64_t _next_ssid; // 會話ID
    std::mutex _mutex;
    std::unordered_map<uint64_t, session_ptr> _session; // 通过会话ID找session
    wsserver_t *_server;
};